/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-08-31     zmake       the first version
 */
#include <rtthread.h>
#include <lwip/sockets.h>

#define DBG_TAG "ota"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#define FIRMWARE_FRAME_SIZE 1024
#define FIRMWARE_MAX_SIZE FIRMWARE_FRAME_SIZE*256

uint32_t str2int(uint8_t *str)
{
    uint8_t *str_end, *str_start;
    uint32_t val=0;
    str_start = str;
    while(*str!='\r')
    {
        str++;
    }
    str_end = str;
    val = strtoll((const char *)str_start, (char **)&str_end, 10);
    if(val == 0)
    {
        rt_kprintf("str转int失败\r\n");
    }
    return val;
}
uint32_t get_bin_size(uint8_t *response)
{
    uint8_t *data_ptr=NULL;
    data_ptr = (uint8_t*)rt_strstr((const char*)response,"Content-Length: ");
    data_ptr += 16;
    uint32_t bin_size = str2int(data_ptr);
    rt_kprintf("[bin size:%ld Bytes]\r\n",bin_size);
    return bin_size;
}

void ota_thread_func(void *arg)
{
    uint16_t socket_desc;
    struct sockaddr_in server;
    char server_ip[] = "124.222.71.199";
    int server_port = 8070;
    char file_path[] = "/stm32/rtthread.bin";
    char *request;
    uint16_t request_size;
    uint8_t *response;

    // 创建套接字
    socket_desc = lwip_socket(AF_INET, SOCK_STREAM, 0);
    if (socket_desc == -1) {
        rt_kprintf("无法创建套接字\n");
        rt_thread_delete(rt_thread_self());
    }

    server.sin_addr.s_addr = inet_addr(server_ip);
    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);

    // 连接服务器
    if (lwip_connect(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0) {
        rt_kprintf("连接失败\n");
        goto ota_failed;
    }

    request_size = rt_snprintf(NULL, 0, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", file_path, server_ip) + 1;

    // 动态申请内存
    request = (char *)rt_malloc(request_size);
    if (request == NULL) {
        rt_kprintf("内存分配失败\n");
        goto ota_failed;
    }

    // 构建HTTP请求
    rt_snprintf(request, request_size, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", file_path, server_ip);
    // 发送HTTP请求
    if (lwip_send(socket_desc, request, rt_strlen(request), 0) < 0) {
        rt_kprintf("发送失败\n");
        goto ota_failed;
    }

    rt_free(request);// 释放请求
    response = (uint8_t *)rt_malloc(FIRMWARE_FRAME_SIZE);
    if(response == NULL)
    {
        rt_kprintf("malloc failed\r\n");
        goto ota_failed;
    }

    uint32_t bytes_received = 0;

    // 读取响应数据头
    bytes_received = lwip_recv(socket_desc, response, 1024, 0);
    if (bytes_received <= 0) {
        rt_kprintf("无法接收响应数据\n");
        goto ota_failed;
    }
    rt_kprintf("%s\r\n", response);

    // 读取响应数据包
    // 获取数据包大小
    uint32_t bin_size_total = 0;
    uint32_t bin_size_count=0;
    bin_size_total = get_bin_size(response);

    // 计算数据帧大小
    uint16_t bin_frame_count=0;
    uint16_t bin_frame_total = bytes_received - total_size / FIRMWARE_FRAME_SIZE;
    rt_kprintf("共有%d帧\r\n",bin_frame_total);

    while(total_size < bin_size)
    {
        uint8_t *bin_data_ptr;
        if(bin_frame_count==bin_frame_total)
            bytes_received = lwip_recv(socket_desc, response, bin_size-total_size, 0);
        else{
            bytes_received = lwip_recv(socket_desc, response, FIRMWARE_FRAME_SIZE, 0);
        }

        if (bytes_received <= 0) {
            rt_kprintf("无法接收响应数据\n");
            goto ota_failed;
        }

        bin_data_ptr = response;
        for(int i=0;i<bytes_received;i++)
        {
            rt_kprintf("%02x", *bin_data_ptr++);
        }
//        rt_kprintf("\r\n");
//        rt_kprintf("第%d帧\r\n",bin_frame_count++);
        total_size += bytes_received;
    }

    // 传输结束，释放内存，关闭连接
    rt_free(response);
    lwip_close(socket_desc);
    rt_kprintf("数据保存完成\n");

ota_failed:
    rt_free(response);

    lwip_close(socket_desc);

    rt_kprintf("出错\r\n");
}

void ota(void)
{
    rt_thread_t ota_thread;
    ota_thread = rt_thread_create("ota", ota_thread_func, RT_NULL, 2048, 5, 20);
    if(ota_thread != RT_NULL)
    {
        rt_thread_startup(ota_thread);
    }else
    {
        LOG_E("ota thread create failed\r\n");
    }
}
MSH_CMD_EXPORT(ota, start ota);

